//
// This file is part of cpp-lib, a library of C++ functions and classes.
// Cpp-lib is copyright (C) 2004 and onwards, The CPL Interest Group.
// 
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or (at
// your option) any later version.
// 
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
// General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this library (see the file COPYING); if not, write to the
// Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
// 02111-1307  USA
//
// $Id$
//

#include <exception>
#include <stdexcept>
#include <string>
#include <sstream>

#include <cmath>
#include <cstring>
#include <cerrno>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>

#include <string.h>

#include "cpp-lib/util.h"
#include "cpp-lib/posix/wrappers.h"
#include "cpp-lib/sys/file.h"
#include "cpp-lib/sys/util.h"


int const OS_ERROR_BUFSIZE = 1024 ;


using namespace cpl::detail_ ;
using namespace cpl::util    ;


void cpl::detail_::strerror_exception
( std::string const& s , int const errnum ) {

  throw std::runtime_error
  ( s + ": " + std::strerror( errnum ? errnum : errno ) ) ;

}


#if 0
std::string const cpl::detail_::os_message( int const errnum ) {

  std::vector< char > v( OS_ERROR_BUFSIZE ) ;

  int const res = ::strerror_r( errnum , &v[ 0 ] , v.size() ) ;

  if( !res ) { return &v[ 0 ] ; }

  return "strerror_r() message too long" ;

}
#endif


double const cpl::detail_::modification_time( int const fd ) {

  struct stat buf ;

  if( ::fstat( fd , &buf ) < 0 ) { strerror_exception( "fstat" ) ; }

  return buf.st_mtime ;

}


int cpl::detail_::posix_open( 
  std::string             const& name ,
  std::ios_base::openmode const  om 
) {

  if( 0 == om || om != ( om & ( std::ios_base::out | std::ios_base::in ) ) )
  { throw std::runtime_error( "bad file open mode" ) ; }

  int flags = 0 ;
  if     ( std::ios_base::out == om ) { flags = O_WRONLY ; }
  else if( std::ios_base::in  == om ) { flags = O_RDONLY ; }
  else                                { flags = O_RDWR   ; }

  int fd = ::open( name.c_str() , flags ) ;

  if( fd < 0 ) { strerror_exception( "open " + name ) ; }

  return fd ;

}


::timespec const cpl::detail_::to_timespec( double const& t ) {

  ::timespec ret ;
  to_fractional( t , 1000000000 , ret.tv_sec , ret.tv_nsec ) ;
  return ret ;

}


::timeval const cpl::detail_::to_timeval( double const& t ) {

  ::timeval ret ;
  to_fractional( t , 1000000 , ret.tv_sec , ret.tv_usec ) ;
  return ret ;

}


void cpl::util::sleep( double const& t ) {

  assert( t >= 0 ) ;

  ::timespec const tt = to_timespec( t ) ;

  if( -1 == ::nanosleep( &tt , 0 ) )
  { strerror_exception( "sleep" ) ; }

}


double cpl::util::time() {

  ::timeval t ;

  assert( 0 == ::gettimeofday( &t , 0 ) ) ;

  return to_double( t ) ;

}


::sigset_t const cpl::detail_::block_signal( int const s ) {

  ::sigset_t ret ;

  // sigemptyset() and sigaddset() may be macros!
  STRERROR_CHECK( sigemptyset( &ret     ) ) ;
  STRERROR_CHECK( sigaddset  ( &ret , s ) ) ;

  int const err = ::pthread_sigmask( SIG_BLOCK , &ret , 0 ) ;
  if( err ) { strerror_exception( "pthread_sigmask()" , err ) ; }

  return ret ;
 
}


long cpl::detail_::posixbuf_traits::read
( resource_type& fd , char      * const buf , long const n ) {
  
  long ret ;
  do { ret = ::read( fd , buf , n ) ; } 
  while( EINTR_repeat( ret ) ) ;

  assert( -1 <= ret      ) ;
  assert(       ret <= n ) ;

  return ret ;

}


long cpl::detail_::posixbuf_traits::write
( resource_type& fd , char const* const buf , long const n ) {
  
  long ret ;

  // TODO: partial send
  do { ret = ::write( fd , buf , n ) ; } 
  while( EINTR_repeat( ret ) ) ;

  if( ret < 0 ) 
  { return -1 ; }

  assert( n == ret ) ;
  return ret ;

}
